package dryven.sapi.servlet_v25;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import dryven.request.http.Cookie;
import dryven.request.http.HttpUtils;
import dryven.request.http.Request;
import dryven.request.http.Session;
import dryven.util.ArrayIterable;
import dryven.util.EnumerationIterable;

public class ServletRequest implements Request {

	private HttpServletRequest request;
	private Cookie[] cookies = null;
	private Map<String, String> urlParameters = null;
	private Set<String> bodyParameterNames = new HashSet<String>();
	private Session sessionWrapper = null;
	
	public ServletRequest(HttpServletRequest request) {
		super();
		this.request = request;
		seperateURLAndBodyParams();
	}
	
	private void seperateURLAndBodyParams() {
		//parse querystring and save in map
		urlParameters = new HashMap<String, String>();
		String queryString = request.getQueryString();
		if(queryString!=null) {
			String[] queryStringParts = queryString.split("&");
			for (String qsp : queryStringParts) {
				int equalsIndex = qsp.indexOf('=');
				String paramName = null;
				//ignore zero-length param name
				if(equalsIndex==0) {
					continue;
				}
				else if(equalsIndex==-1) {
					paramName = qsp;
				} else {
					paramName = qsp.substring(0, equalsIndex);
				}
				paramName = HttpUtils.URLDecode(paramName);
				urlParameters.put(paramName, request.getParameter(paramName));
			}
		}
		//create enumerable with only body params
		Enumeration<String> requestParamNames = request.getParameterNames();
		while(requestParamNames.hasMoreElements()) {
			String name = requestParamNames.nextElement();
			if(!urlParameters.containsKey(name)) {
				bodyParameterNames.add(name);
			}
		}
	}

	@Override
	public int getContentLength() {
		return request.getContentLength();
	}

	@Override
	public Cookie getCookie(String name) {
		for (Cookie cookie : getCookies()) {
			if(cookie.getName().equals(name)) {
				return cookie;
			}
		}
		return null;
	}

	@Override
	public Iterable<Cookie> getCookies() {
		if(cookies==null) {
			cookies = new Cookie[request.getCookies().length];
			int index = 0;
			for (javax.servlet.http.Cookie c : request.getCookies()) {
				cookies[index] = new ServletCookie(c);
				++index;
			}
		}
		return new ArrayIterable<Cookie>(cookies);
	}

	@Override
	public String getURLParam(String name) {
		return urlParameters.get(name);
	}

	@Override
	public Iterable<String> getURLParamNames() {
		return urlParameters.keySet();
	}

	@Override
	public String getHeader(String name) {
		return request.getHeader(name);
	}

	@Override
	public Iterable<String> getHeaderNames() {
		return new EnumerationIterable<String>(request.getHeaderNames());
	}

	@Override
	public InputStream getInputStream() {
		try {
		return request.getInputStream();
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public String getLocalName() {
		return request.getLocalName();
	}

	@Override
	public int getLocalPort() {
		return request.getLocalPort();
	}

	@Override
	public HttpMethod getMethod() {
		if(request.getMethod().equalsIgnoreCase("POST")) {
			return HttpMethod.Post;
		} else if(request.getMethod().equalsIgnoreCase("GET")) {
			return HttpMethod.Get;
		} else if(request.getMethod().equalsIgnoreCase("PUT")) {
			return HttpMethod.Put;
		} else if(request.getMethod().equalsIgnoreCase("DELETE")) {
			return HttpMethod.Delete;
		} else if(request.getMethod().equalsIgnoreCase("HEAD")) {
			return HttpMethod.Head;
		} else {
			throw new RuntimeException(String.format("Invalid HTTP method: %s",request.getMethod()));
		}
	}

	@Override
	public String getBodyParam(String name) {
		if(urlParameters.containsKey(name)) {
			return null;
		} else {
			return request.getParameter(name);
		}
	}

	@Override
	public Iterable<String> getBodyParamNames() {
		return bodyParameterNames;
	}

	@Override
	public String getRemoteHost() {
		return request.getRemoteHost();
	}

	@Override
	public int getRemotePort() {
		return request.getRemotePort();
	}
	
	@Override
	public String getRemoteUser() {
		return request.getRemoteUser();
	}

	@Override
	public String getRequestURI() {
		return request.getRequestURI();
	}

	@Override
	public Session getSession() {
		if(sessionWrapper==null) {
			sessionWrapper = new ServletSessionWrapper(request.getSession());
		}
		return sessionWrapper;
	}

	@Override
	public String getLocalApplicationURL() {
		return request.getRequestURI().substring(request.getContextPath().length());
	}

	@Override
	public Object getAttribute(String name) {
		return request.getAttribute(name);
	}

	@Override
	public void setAttribute(String name, Object value) {
		request.setAttribute(name, value);
	}

	@Override
	public Iterable<String> getAttributeNames() {
		return new EnumerationIterable<String>(request.getAttributeNames());
	}
}
